package fem2.jlinalg;

import fem2.NotImplementedError;
import inf.jlinalg.IMatrix;
import inf.jlinalg.IMatrixReadOnly;

public class CSCMatrix implements IMatrix {

	private static final long serialVersionUID = 1L;

	private int nRows;
	private int nCols;
	private int nZeros;
	private int[] colPtr;
	private int[] rowInd;
	private double[] values;

	/**
	 * constructor for empty matrix
	 * 
	 * @param nRows
	 * @param colptr
	 * @param rowind
	 */
	public CSCMatrix(int nRows, int[] colptr, int[] rowind) {
		this.nRows = nRows;
		this.nCols = colptr.length - 1;
		this.nZeros = rowind.length;
		this.colPtr = colptr;
		this.rowInd = rowind;
		this.values = new double[nZeros];
	}

	/**
	 * copy constructor
	 * 
	 * @param m
	 */
	public CSCMatrix(CSCMatrix m) {
		this.nRows = m.getRowCount();
		this.nCols = m.getColumnCount();
		this.nZeros = m.getNonzeros();
		this.colPtr = m.columnPointer().clone();
		this.rowInd = m.rowIndex().clone();
		this.values = m.values().clone();
	}

	@Override
	public double get(int i, int j) {
		int s = colPtr[j] - colPtr[0];
		int l = colPtr[j + 1] - colPtr[j];
		for (int k = 0; k < l; k++) {
			if (rowInd[s + k] == i) {
				return values[s + k];
			}
		}
		return 0;
	}

	@Override
	public int getColumnCount() {
		return this.nCols;
	}

	@Override
	public int getRowCount() {
		return this.nRows;
	}

	public int getNonzeros() {
		return this.nZeros;
	}

	@Override
	public void add(int i, int j, double value) {
		int s = colPtr[j] - colPtr[0];
		int l = colPtr[j + 1] - colPtr[j];
		for (int k = 0; k < l; k++) {
			if (rowInd[s + k] == i) {
				values[s + k] += value;
			}
		}
	}

	@Override
	public void set(int i, int j, double value) {
		int s = colPtr[j] - colPtr[0];
		int l = colPtr[j + 1] - colPtr[j];
		for (int k = 0; k < l; k++) {
			if (rowInd[s + k] == i) {
				values[s + k] = value;
			}
		}
	}

	@Override
	public void assign(IMatrixReadOnly a) {
		/*
		 * TODO
		 */
		throw new NotImplementedError();
	}

	@Override
	public Object clone() {
		return new CSCMatrix(this);
	}

	/**
	 * reset all values of the matrix to zero
	 */
	public void initialize() {
		for (int i = 0; i < values.length; i++) {
			values[i] = 0;
		}
	}

	/**
	 * get the column pointer
	 * 
	 * @return
	 */
	public int[] columnPointer() {
		return this.colPtr;
	}

	/**
	 * get the row index
	 * 
	 * @return
	 */
	public int[] rowIndex() {
		return this.rowInd;
	}

	/**
	 * get the values array
	 * 
	 * @return
	 */
	public double[] values() {
		return this.values;
	}

	/**
	 * print the matrix in general form
	 */
	public void print() {
		for (int i = 0; i < nRows; i++) {
			for (int j = 0; j < nCols; j++) {
				System.out.print(get(i, j) + " ");
			}
			System.out.println();
		}
	}

	/**
	 * print the matrix in compact form
	 */
	public void printCompact() {
		System.out.print("{");
		for (int i = 0; i < colPtr.length; i++) {
			if (i < colPtr.length - 1) {
				System.out.print(colPtr[i] + ",");
			} else {
				System.out.print(colPtr[i]);
			}
		}
		System.out.print("}");
		System.out.println();
		System.out.print("{");
		for (int i = 0; i < rowInd.length; i++) {
			if (i < rowInd.length - 1) {
				System.out.print(rowInd[i] + ",");
			} else {
				System.out.print(rowInd[i]);
			}
		}
		System.out.print("}");
		System.out.println();
		System.out.print("{");
		for (int i = 0; i < values.length; i++) {
			if (i < values.length - 1) {
				System.out.print(values[i] + ",");
			} else {
				System.out.print(values[i]);
			}
		}
		System.out.print("}");
		System.out.println();
	}

}
